iT邦幫忙

2022 iThome 鐵人賽

DAY 5
0
Modern Web

速成 Phoenix, 2022年最受喜愛框架系列 第 5

{05, Elixir, "Pattern matching (模式比對)"}

  • 分享至 

  • xImage
  •  

最剛開始提到的使用 = 賦予變數值,其實有點不對
在 Elixir 裡, = 用右邊的值來比對左邊,不僅可以用在單一的值如

name = "小明"

也可以用在前面有提到比較複雜的資料結構

%{name: name} = %{name: "小明"}
name #=> "小明"

https://ithelp.ithome.com.tw/upload/images/20220909/20141054vgCyygE3OY.png

可以想像成給小孩玩的形狀配對玩具,拿上一個 %{name: "小明"} 的例子來說,左邊的目標符合右邊的資料結構,所以 "小明" 就會被附在變數 name 上面,在第一個單純的例子,因為左邊只有變數 name ,所以不管右邊放什麼都會是符合的,所以用起來就會像其他語言的變數賦值。

所以這行是可以成立的,因為左邊符合右邊,這個大部分其他語言是不行的

123 = 123

當然這樣就不行了

0 = 123
#=> ** (MatchError) no match of right hand side value: 123

如果配對失敗呢?
配對失敗時, elixir 會跳出 MatchError 錯誤,如果是在多個條件裡,他會自動去比對下一個,這個待會後面詳述。

pattern matching 使用在 elixir 的各個角落,這邊來提幾個常見的狀態

從複雜的 map 提取中間的值

之後再處理資料或是網頁要求的時候,常常會使用,假設我們有一個客戶資料

client = %{name: "阿強", number: "0900000000", address: "16 Rich St."}

如果這次只需要用到 name 跟 number ,我們可以這樣提取

%{name: name, number: number} = client
name #=> "阿強"
number #=> "0900000000"

case 判斷也用

因為 pattern matching 的關係, case 變得相當方便,可以輕鬆寫出複雜的比對條件,這邊一樣使用客戶阿強

client = %{name: "阿強", number: "0900000000", address: "16 Rich St."}

case client do
  %{name: "小林"} -> "VIP 客戶"
  %{number: "0900000000"} -> "這個電話是阿強的電話"
  others -> "普通客戶"
end
#=> "這個電話是阿強的電話"

在這邊 elixir 會試著由上至下去比對哪一個條件符合,先符合就不會執行後面了,

  1. 先比對 %{name: "小林"} ,雖然有 name ,但是裡面的值不符合,比對失敗換下一個
  2. 比對 %{number: "0900000000"} ,比對成功! 這個 case 在這邊結束,回傳 "這個電話是阿強的電話",後面的條件就不再執行

因為比對是由上至下,我們通常都會在最後一個使用單一個變數來接住前面全部不符合的情況,如果前面都不符合也沒有要用到最後接住的值,通常會用 _ 來接。

system = %{status: :running}

case system do
  %{status: :error} -> "出大事拉"
  _ -> "沒事"
end

呼叫 function 也用

在 elixir 裡面,我們允許同名方法,因為方法的引述(argument)也是使用 pattern matching,
如果用 case 我們可以這樣寫:

def greeting(name, level) do
  case level do
    2 -> "親愛的 #{name} ,歡迎"
    1 -> "你好 #{name}"
    _ -> "喔 你來了喔 #{name}"
  end
end

如果改用同名方法,可以改寫成
```elixir
def greeting(name, 2), do: "親愛的 #{name} ,歡迎"
def greeting(name, 1), do: "你好 #{name}"
def greeting(name, _), do: "你好 #{name}"

如果呼叫 greeting 方法

greeting("Linda", 1)
#=> "你好 Linda"

他會從第一個開始照順序試,在這個例子,我們匹配到第二個

複雜情況的用法

=

像下面這個資料,client 裡面 company 的值是另一個 map,
如果我們想要取 company 全部,與他裡面的電話可以在左邊使用 =

client = %{name: "John", company: %{name: "醬公司", number: "04-22223333"}}

%{company: company = %{number: number}} = client
company #=> %{name: "醬公司", number: "04-22223333"}
number #=> "04-22223333"

pin ^

有得時候左邊要拿來當條件的值本身是變數的時候,可以用 caret 符號 ^ 把他固定住

number = 2
client = %{number: 1, name: "Amelia"}

case client do
  %{number: ^number} -> "你中獎拉!"
  _ -> "可憐沒中"
end

如果上面的 number 沒有使用 ^ 固定住的話,不管 number 是多少都會比對正確,並重新幫 number 附上新的值。
但因為有 pin 住 number ,所以匹配的時候他被當成 2 來匹配。


上一篇
{04, Elixir, "模組與方法"}
下一篇
{06, Phoenix, "安裝與開發設定"}
系列文
速成 Phoenix, 2022年最受喜愛框架30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言